#include "stdafx.h"
#include "RecursiveMutex.h"
#ifdef WIN32
# include <Windows.h>
#else
# include <errno.h>
# include <common/TimeSpec.h>
#endif

namespace catan
{
	RecursiveMutex::RecursiveMutex()
	{
#ifdef WIN32
		mMutex = ::CreateMutex(0, FALSE, 0);
#else
		pthread_mutexattr_t attr;
		pthread_mutexattr_init(&attr);
		pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
		pthread_mutex_init(&mMutex, &attr);
		pthread_mutexattr_destroy(&attr);
#endif
	}

	RecursiveMutex::~RecursiveMutex()
	{
#ifdef WIN32
		::CloseHandle(mMutex);
#else
		pthread_mutex_destroy(&mMutex);
#endif
	}

	LockState::Enum RecursiveMutex::Acquire(long timeout)
	{
#ifdef WIN32
		DWORD ret = ::WaitForSingleObject(mMutex, timeout);
		switch (ret)
		{
		case WAIT_OBJECT_0:
			return LockState::SUCCEEDED;
		case WAIT_TIMEOUT:
			return LockState::TIMEDOUT;
		case WAIT_ABANDONED:
			return LockState::OWNERTERM;
		default:
			return LockState::FAILED;
		}
#else
		int ret = 0;
		if (timeout == -1)
		{
			ret = pthread_mutex_lock(&mMutex);
		}
		else
		{
			TimeSpec t = TimeSpec::MakeTimeout(timeout);
			ret = pthread_mutex_timedlock(&mMutex, &t);
		}
		switch (ret)
		{
		case 0:
			return LockState::SUCCEEDED;
		case ETIMEDOUT:
			return LockState::TIMEDOUT;
		default:
			return LockState::FAILED;
		}
#endif
	}

	LockState::Enum RecursiveMutex::TryAcquire()
	{
#ifdef WIN32
		DWORD ret = ::WaitForSingleObject(mMutex, 0);
		switch (ret)
		{
		case WAIT_OBJECT_0:
			return LockState::SUCCEEDED;
		case WAIT_TIMEOUT:
			return LockState::BUSY;
		case WAIT_ABANDONED:
			return LockState::OWNERTERM;
		default:
			return LockState::FAILED;
		}
#else
		int ret = pthread_mutex_trylock(&mMutex);
		switch (ret)
		{
		case 0:
			return LockState::SUCCEEDED;
		case EBUSY:
			return LockState::BUSY;
		case ETIMEDOUT:
			return LockState::TIMEDOUT;
		default:
			return LockState::FAILED;
		}
#endif
	}

	void RecursiveMutex::Release()
	{
#ifdef WIN32
		::ReleaseMutex(mMutex);
#else
		pthread_mutex_unlock(&mMutex);
#endif
	}
}
